package com.yzs.exam.config.spring.security;

import com.yzs.exam.config.property.CookieConfig;
import com.yzs.exam.config.property.SystemConfig;
import com.yzs.exam.entity.enums.RoleEnum;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Collections;
import java.util.List;

/**
 * security配置类
 * @author yzs
 * @create 2021-03-19 17:09
 */
@Configuration
@EnableWebSecurity //开启security配置
public class SecurityConfig{

    /**
     * 继承WebSecurityConfigurerAdapter,重新方法
     * 哪些用户需要经过身份验证，通过何种方式验证
     */
    @Configuration
    @AllArgsConstructor
    public  class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        private final SystemConfig systemConfig;
        private final LoginAuthenticationEntryPoint restAuthenticationEntryPoint;
        private final RestAuthenticationProvider restAuthenticationProvider;
        private final RestDetailsServiceImpl formDetailsService;
        private final RestAuthenticationSuccessHandler restAuthenticationSuccessHandler;
        private final RestAuthenticationFailureHandler restAuthenticationFailureHandler;
        private final RestLogoutSuccessHandler restLogoutSuccessHandler;
        private final RestAccessDeniedHandler restAccessDeniedHandler;


        /**
         * 定义认证规则
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //表示该页面可以在指定来源的 frame 中展示
            http.headers().frameOptions().disable();
            //获取系统配置
            List<String> securityIgnoreUrls = systemConfig.getSecurityIgnoreUrls();
            String[] ignores = new String[securityIgnoreUrls.size()];
            //添加自定义的登录过滤（记住我） -- 以及用户密码认真过滤器 -- 将A拦截器添加到B拦截器的位置
            //获取对应异常，未授权统一管理，响应未授权错误信息
            //委托者模式：AuthenticationManager通过roviderManager得到AuthenticationProvider的认证方式进行用户认证
            //认证所有请求
            //securityIgnoreUrls -- 无需认证保护
            //admin、student文件下的url需要所对应角色
            //拒绝异常访问处理
            //表单登录成功后的处理和失败后的处理
            //注销对应url和注销成功的处理类，要在退出登录后让当前 session 失效
            //开启记住我功能，并设置token有效时长
            //关闭csrf，防止跨域伪造请求
            //有限开放部分资源共享（与同源策略相反）
            http
                    .addFilterAt(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                    .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)
                    .and().authenticationProvider(restAuthenticationProvider)
                    .authorizeRequests()
                    .antMatchers(securityIgnoreUrls.toArray(ignores)).permitAll()
                    .antMatchers("/api/admin/**").hasRole(RoleEnum.ADMIN.getName())
                    .antMatchers("/api/student/**").hasRole(RoleEnum.STUDENT.getName())
                    .anyRequest().permitAll()
                    .and().exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
                    .and().formLogin().successHandler(restAuthenticationSuccessHandler).failureHandler(restAuthenticationFailureHandler)
                    .and().logout().logoutUrl("/api/user/logout").logoutSuccessHandler(restLogoutSuccessHandler).invalidateHttpSession(true)
                    .and().rememberMe().key(CookieConfig.getName()).tokenValiditySeconds(CookieConfig.getInterval()).userDetailsService(formDetailsService)
                    .and().csrf().disable()
                    .cors();
        }

        //开启http.cors() -- 注入一个cors过滤器
        @Bean
        public CorsConfigurationSource corsConfigurationSource(){
            final CorsConfiguration configuration = new CorsConfiguration();
            configuration.setMaxAge(3600L);
            configuration.setAllowedOrigins(Collections.singletonList("*"));
            configuration.setAllowedMethods(Collections.singletonList("*"));
            configuration.setAllowCredentials(true);
            configuration.setAllowedHeaders(Collections.singletonList("*"));
            final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/api/**", configuration);
            return source;
        }


        //认证过滤器，封装认证对象、设置认证成功处理类、认证失败处理类、认证管理器、定制用户详细信息
        @Bean
        public RestLoginAuthenticationFilter authenticationFilter() throws Exception {
            RestLoginAuthenticationFilter authenticationFilter = new RestLoginAuthenticationFilter();
            //认证成功的执行
            authenticationFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler);
            //认证失败的执行
            authenticationFilter.setAuthenticationFailureHandler(restAuthenticationFailureHandler);
            //设置对应认证管理器
            authenticationFilter.setAuthenticationManager(authenticationManagerBean());
            //使用记住我功能时候,需要制定一个UserDetailsService
            authenticationFilter.setUserDetailsService(formDetailsService);
            return authenticationFilter;
        }

    }

}
